package com.rock.code.common.util;

//import com.sitech.ismp.coll.net.cisco.NetEventEntry;
//import org.apache.commons.lang.StringUtils;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;

import com.rock.code.common.util.snmp.IfEntry;
import org.apache.commons.lang.StringUtils;
import org.snmp4j.AbstractTarget;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.*;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;


import java.io.IOException;
import java.util.*;

/**
 * SnmpClient snmp = SnmpClient.create("v2c").setHost("127.0.0.1").setPort("161").setCommunity("public");
 * <p>
 * try {
 * snmp.connect();
 * snmp.walk("1.3.6.1.2.1");
 * } finally {
 * snmp.close();
 * }
 */
public abstract class SnmpClient {

    public static SnmpClient create(String version) {
        if (StringUtils.containsIgnoreCase(version, "v2c")) {
            return new SnmpClientV2c().setVersion(SnmpConstants.version2c);
        } else if (StringUtils.containsIgnoreCase(version, "3")) {
            return new SnmpClientV3().setVersion(SnmpConstants.version3);
        } else {
            return new SnmpClientV2c().setVersion(SnmpConstants.version2c);
        }
    }

//	protected Logger logger = LoggerFactory.getLogger(getClass());

    protected String host;

    protected String port;

    protected AbstractTarget target;

    protected Snmp snmp;

    // 0:version1, 1:version2c, 3:version3
    protected int version;

    // v1/v2c使用
    protected String community;

    // 1:SecurityLevel.NOAUTH_NOPRIV, 2:SecurityLevel.AUTH_NOPRIV, 3:SecurityLevel.AUTH_PRIV
    protected int securityLevel;

    protected OctetString serurityName;

    // MD5,SHA
    protected OID authenticationProtocol;
    protected OctetString authenticationPassphrase;

    // AES128,AES192,AES256,DES
    protected OID privacyProtocol;
    protected OctetString privacyPassphrase;

    public abstract SnmpClient connect() throws IOException;

    public void close() {
        if (snmp != null) {
            try {
                snmp.close();
            } catch (IOException e) {
//				logger.warn("Exception while close snmp connection => udp:{}/{}", host, port, e);
            }
        }
    }

    protected abstract PDU createPDU();

    protected abstract PDU createPDU(int type);

    public String get(String oid) throws Exception {
//		logger.info("get {}", oid);
        PDU pdu = createPDU();
        pdu.add(new VariableBinding(new OID(oid)));

        ResponseEvent response = snmp.send(pdu, target);

        if (null == response || null == response.getResponse()) {
            throw new Exception("SNMP response is null: get " + oid);
        }

        Vector<VariableBinding> recVBs = response.getResponse().getVariableBindings();

        VariableBinding recVB = recVBs.elementAt(0);

//		logger.info(oid + " => " + recVB.getVariable().toString());
        return StringUtils.trimToEmpty(recVB.getVariable().toString());
    }

    public Map<String, String> walk(String oid) throws Exception {
        return walk(oid, false);
    }

    /**
     * @param oid
     * @param isPrintable 用于处理中文字符串的问题
     * @return
     * @throws Exception
     */
    public Map<String, String> walk(String oid, boolean isPrintable) throws Exception {
        Map<String, String> result = new HashMap<String, String>();

        PDU pdu = createPDU(PDU.GETNEXT);
        pdu.add(new VariableBinding(new OID(oid)));

        int subOidIdx = oid.length() + 1;
        while (true) {
            ResponseEvent response = snmp.send(pdu, target);
            if (response == null || response.getResponse() == null) {
                break;
            }

            Vector<VariableBinding> vbs = response.getResponse().getVariableBindings();
            if (null == vbs || vbs.isEmpty()) {
                break;
            }

            VariableBinding recVB = vbs.elementAt(0);
            String rspOid = recVB.getOid().toString();
            Object obj = recVB.getVariable();
            String rspValue = obj.toString();
            if (obj instanceof OctetString) {
                if (rspOid.startsWith("1.3.6.1.2.1.2.2.1.2.")) {
                    rspValue = new String(((OctetString) recVB.getVariable()).getValue(), "GBK");
                }
                if (rspOid.startsWith("1.3.6.1.2.1.2.2.1.6.")) {
                    byte[] temp = ((OctetString) recVB.getVariable()).getValue();
                    if (temp.length > 0) {
                        rspValue = String.format("%02x:%02x:%02x:%02x:%02x:%02x", temp[0], temp[1], temp[2], temp[3], temp[4], temp[5]);
                    }
                }
            }

            if (!StringUtils.startsWith(rspOid, oid + ".")) {
                break;
            }
            System.out.println(rspOid.substring(subOidIdx)+":"+rspValue);
            result.put(rspOid.substring(subOidIdx), rspValue);
            pdu.clear();
            pdu.add(new VariableBinding(new OID(rspOid)));
        }
        return result;
    }

    public List<IfEntry> walkIfTable(String ip,String scId) {
        List<IfEntry> ifTable = new ArrayList<IfEntry>();

        Set<String> oidSet = new HashSet<String>();

        IfEntry ifEntry;
        while ((ifEntry = getIfEntry(oidSet)) != null) {
//            ifEntry.setIeId(RandomGUID.getRandomGUID());
            ifEntry.setScId(scId);
            ifEntry.setFromIp(ip);
            System.out.println(ifEntry);
            ifTable.add(ifEntry);
        }
        return ifTable;
    }


    public IfEntry getIfEntry(Set<String> oidSet) {
        IfEntry ifEntry = new IfEntry();

        if (oidSet.isEmpty()) {
            oidSet.add("1.3.6.1.2.1.2.2.1.1");
            oidSet.add("1.3.6.1.2.1.2.2.1.2");
            oidSet.add("1.3.6.1.2.1.2.2.1.3");
//			oidSet.add("1.3.6.1.2.1.2.2.1.5");
            oidSet.add("1.3.6.1.2.1.2.2.1.6");
//			oidSet.add("1.3.6.1.2.1.2.2.1.7");
//			oidSet.add("1.3.6.1.2.1.2.2.1.8");
//			oidSet.add("1.3.6.1.2.1.2.2.1.10");
//			oidSet.add("1.3.6.1.2.1.2.2.1.11");
//			oidSet.add("1.3.6.1.2.1.2.2.1.12");
//			oidSet.add("1.3.6.1.2.1.2.2.1.13");
//			oidSet.add("1.3.6.1.2.1.2.2.1.14");
//			oidSet.add("1.3.6.1.2.1.2.2.1.16");
//			oidSet.add("1.3.6.1.2.1.2.2.1.17");
//			oidSet.add("1.3.6.1.2.1.2.2.1.18");
//			oidSet.add("1.3.6.1.2.1.2.2.1.19");
//			oidSet.add("1.3.6.1.2.1.2.2.1.20");
        }

        Map<String, String> vbs = getNext(oidSet);

        if (null == vbs || vbs.isEmpty()) {
            return null;
        }

        oidSet.clear();
        oidSet.addAll(vbs.keySet());

        for (String oid : vbs.keySet()) {
            if (oid.startsWith("1.3.6.1.2.1.2.2.1.1.")) {
                ifEntry.setIfIndex(vbs.get(oid));
            } else if (oid.startsWith("1.3.6.1.2.1.2.2.1.2.")) {
                ifEntry.setIfDescr(vbs.get(oid));
            } else if (oid.startsWith("1.3.6.1.2.1.2.2.1.3.")) {
                ifEntry.setIfType(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.5.")) {
//				ifEntry.setIfSpeed(vbs.get(oid));
            } else if (oid.startsWith("1.3.6.1.2.1.2.2.1.6.")) {
                ifEntry.setIfPhysAddress(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.7.")) {
//				ifEntry.setIfAdminStatus(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.8.")) {
//				ifEntry.setIfOperStatus(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.10.")) {
//				ifEntry.setIfInOctets(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.11.")) {
//				ifEntry.setIfInUcastPkts(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.12.")) {
//				ifEntry.setIfInNUcastPkts(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.13.")) {
//				ifEntry.setIfInDiscards(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.14.")) {
//				ifEntry.setIfInErrors(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.16.")) {
//				ifEntry.setIfOutOctets(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.17.")) {
//				ifEntry.setIfOutUcastPkts(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.18.")) {
//				ifEntry.setIfOutNUcastPkts(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.19.")) {
//				ifEntry.setIfOutDiscards(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.2.1.2.2.1.20.")) {
//				ifEntry.setIfOutErrors(vbs.get(oid));
            } else {
                return null;
            }
        }

        return ifEntry.setTimeMillis(System.currentTimeMillis());
    }


    public Map walkIpAddrTable(String scId, String ip) throws Exception {
        Map result = new HashMap();
        List ipNetToMediaPhysAddressList = new ArrayList();
        List ipNetToMediaTypeList = new ArrayList();
        List ipAddrTableList = new ArrayList();
        Map<String, String> ipNetToMediaPhysAddressMap = walk("1.3.6.1.2.1.4.22.1.2");
        Map<String, String> ipNetToMediaTypeMap = walk("1.3.6.1.2.1.4.22.1.4");
        Map<String, String> ipAddrTableMap = walk("1.3.6.1.2.1.4.20.1.2");
        if (ipNetToMediaPhysAddressMap != null && ipNetToMediaPhysAddressMap.size() != 0) {
            for (Map.Entry<String, String> m : ipNetToMediaPhysAddressMap.entrySet()) {
                String key = m.getKey() == null ? "" : m.getKey();
                String value = m.getValue() == null ? "" : m.getValue();
                if (!"".equals(key) || !"".equals(value)) {
                    int i = key.indexOf(".");
                    String indexNumber = key.substring(0, i);//索引号
                    String oppositeIp = key.substring(i + 1);//对端IP地址
                    String interfaceOppositeMac = value;//对端MAC地址
                    HashMap map = new HashMap();
                    map.put("scId", scId);
                    map.put("fromIp", ip);
//                    map.put("intmpaId", RandomGUID.getRandomGUID());
                    map.put("indexNumber", indexNumber);
                    map.put("oppositeIp", oppositeIp);
                    map.put("interfaceOppositeMac", interfaceOppositeMac);
                    ipNetToMediaPhysAddressList.add(map);
                }
            }
//            System.out.println("ipNetToMediaPhysAddressList" + ipNetToMediaPhysAddressList);
            result.put("ipNetToMediaPhysAddressList", ipNetToMediaPhysAddressList);
        }
        if (ipNetToMediaTypeMap != null && ipNetToMediaTypeMap.size() != 0) {
            for (Map.Entry<String, String> m : ipNetToMediaTypeMap.entrySet()) {
                String key = m.getKey() == null ? "" : m.getKey();
                String value = m.getValue() == null ? "" : m.getValue();
                if (!"".equals(key) || !"".equals(value)) {
                    int i = key.indexOf(".");
                    String indexNumber = key.substring(0, i);//索引号
                    String oppositeIp = key.substring(i + 1);//对端IP地址
                    String type = value;//类型
                    HashMap map = new HashMap();
                    map.put("scId", scId);
                    map.put("fromIp", ip);
//                    map.put("intmtId", RandomGUID.getRandomGUID());
                    map.put("indexNumber", indexNumber);
                    map.put("oppositeIp", oppositeIp);
                    map.put("type", type);
                    ipNetToMediaTypeList.add(map);
                }
            }
//            System.out.println("ipNetToMediaTypeList" + ipNetToMediaTypeList);
            result.put("ipNetToMediaTypeList", ipNetToMediaTypeList);
        }
        if (ipAddrTableMap != null && ipAddrTableMap.size() != 0) {
            for (Map.Entry<String, String> m : ipAddrTableMap.entrySet()) {
                String key = m.getKey() == null ? "" : m.getKey();
                String value = m.getValue() == null ? "" : m.getValue();
                if (!"".equals(key) || !"".equals(value)) {
                    String thisIp = key;//本端接口IP地址
                    String indexNumber = value;//索引号
                    HashMap map = new HashMap();
                    map.put("scId", scId);
                    map.put("fromIp", ip);
//                    map.put("iatId", RandomGUID.getRandomGUID());
                    map.put("indexNumber", indexNumber);
                    map.put("thisIp", thisIp);
                    ipAddrTableList.add(map);
                }
            }
//            System.out.println("ipAddrTableList" + ipAddrTableList);
            result.put("ipAddrTableList", ipAddrTableList);
        }
        return result;
    }

    public Map<String, String> getNext(Set<String> oidSet) {
        Map<String, String> result = new HashMap<String, String>();

        try {
            PDU pdu = createPDU();
            pdu.setType(PDU.GETNEXT);
            for (String oid : oidSet) {
                pdu.add(new VariableBinding(new OID(oid)));
            }

            ResponseEvent response = snmp.send(pdu, target);
            if (response == null || response.getResponse() == null) {
                return null;
            }

            Vector<VariableBinding> recVBs = response.getResponse().getVariableBindings();
            if (recVBs == null || recVBs.size() == 0) {
                return null;
            }

            for (VariableBinding recVB : recVBs) {
                String oid = recVB.getOid().toString();
                String value = recVB.getVariable().toString();

                if (oid.startsWith("1.3.6.1.2.1.2.2.1.2.")) {
                    value = new String(((OctetString) recVB.getVariable()).getValue(), "GBK");
                }

                result.put(recVB.getOid().toString(), value);
            }

            return result;
        } catch (Exception e) {
            throw new RuntimeException("Exception while getIfEntry", e);
        }
    }

    public SnmpClient setCommunity(String community) {
        this.community = community;
        return this;
    }

    public SnmpClient setSecurityLevel(String securityLevel) {
        if ("authNoPriv".equalsIgnoreCase(securityLevel)) {
            this.securityLevel = SecurityLevel.AUTH_NOPRIV;
        } else if ("authPriv".equalsIgnoreCase(securityLevel)) {
            this.securityLevel = SecurityLevel.AUTH_PRIV;
        } else if ("noAuthNoPriv".equalsIgnoreCase(securityLevel)) {
            this.securityLevel = SecurityLevel.NOAUTH_NOPRIV;
        }
        return this;
    }

    public SnmpClient setSerurityName(String serurityName) {
        if (StringUtils.isNotBlank(serurityName)) {
            this.serurityName = new OctetString(serurityName);
        }
        return this;
    }

    public SnmpClient setAuthenticationProtocol(String authenticationProtocol) {
        if ("MD5".equalsIgnoreCase(authenticationProtocol)) {
            this.authenticationProtocol = AuthMD5.ID;
        } else if ("SHA".equalsIgnoreCase(authenticationProtocol)) {
            this.authenticationProtocol = AuthSHA.ID;
        }
        return this;
    }

    public SnmpClient setAuthenticationPassphrase(String authenticationPassphrase) {
        if (StringUtils.isNotBlank(authenticationPassphrase) && !"null".equals(authenticationPassphrase)) {
            this.authenticationPassphrase = new OctetString(authenticationPassphrase);
        }
        return this;
    }

    public SnmpClient setPrivacyProtocol(String privacyProtocol) {
        if ("AES128".equalsIgnoreCase(privacyProtocol)) {
            this.privacyProtocol = PrivAES128.ID;
        } else if ("AES192".equalsIgnoreCase(privacyProtocol)) {
            this.privacyProtocol = PrivAES192.ID;
        } else if ("AES256".equalsIgnoreCase(privacyProtocol)) {
            this.privacyProtocol = PrivAES256.ID;
        } else if ("DES".equalsIgnoreCase(privacyProtocol)) {
            this.privacyProtocol = PrivDES.ID;
        }
        return this;
    }

    public SnmpClient setPrivacyPassphrase(String privacyPassphrase) {
        if (StringUtils.isNotBlank(privacyPassphrase) && !"null".equals(privacyPassphrase)) {
            this.privacyPassphrase = new OctetString(privacyPassphrase);
        }
        return this;
    }

    public SnmpClient setVersion(int version) {
        this.version = version;
        return this;
    }

    public SnmpClient setHost(String host) {
        this.host = host;
        return this;
    }

    public SnmpClient setPort(String port) {
        this.port = port;
        return this;
    }

    public static final int DEFAULT_VERSION = SnmpConstants.version2c;
    public static final String DEFAULT_PROTOCOL = "udp";
    public static final int DEFAULT_PORT = 161;
    public static final long DEFAULT_TIMEOUT = 3 * 1000L;
    public static final int DEFAULT_RETRY = 3;

    public static CommunityTarget createDefault(String ip, String community) {
        Address address = GenericAddress.parse(DEFAULT_PROTOCOL + ":" + ip
                + "/" + DEFAULT_PORT);
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString(community));
        target.setAddress(address);
        target.setVersion(DEFAULT_VERSION);
        target.setTimeout(DEFAULT_TIMEOUT); // milliseconds
        target.setRetries(DEFAULT_RETRY);
        return target;

    }
    public static void snmpGet(String ip, String community, String oid) {

        CommunityTarget target = createDefault(ip, community);
        Snmp snmp = null;
        try {
            PDU pdu = new PDU();
            // pdu.add(new VariableBinding(new OID(new int[]
            // {1,3,6,1,2,1,1,2})));
            pdu.add(new VariableBinding(new OID(oid)));

            DefaultUdpTransportMapping transport = new DefaultUdpTransportMapping();
            snmp = new Snmp(transport);
            snmp.listen();
            System.out.println("-------> 发送PDU <-------");
            pdu.setType(PDU.GET);
            ResponseEvent respEvent = snmp.send(pdu, target);
            System.out.println("PeerAddress:" + respEvent.getPeerAddress());
            PDU response = respEvent.getResponse();

            if (response == null) {
                System.out.println("response is null, request time out");
            } else {

                // Vector<VariableBinding> vbVect =
                // response.getVariableBindings();
                // System.out.println("vb size:" + vbVect.size());
                // if (vbVect.size() == 0) {
                // System.out.println("response vb size is 0 ");
                // } else {
                // VariableBinding vb = vbVect.firstElement();
                // System.out.println(vb.getOid() + " = " + vb.getVariable());
                // }

                System.out.println("response pdu size is " + response.size());
                for (int i = 0; i < response.size(); i++) {
                    VariableBinding vb = response.get(i);
                    System.out.println(vb.getOid() + " = " + vb.getVariable());
                }

            }
            System.out.println("SNMP GET one OID value finished !");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("SNMP Get Exception:" + e);
        } finally {
            if (snmp != null) {
                try {
                    snmp.close();
                } catch (IOException ex1) {
                    snmp = null;
                }
            }

        }

    }
    /*根据OID列表，一次获取多条OID数据，并且以List形式返回*/
    public static void snmpGetList(String ip, String community, List<String> oidList)
    {
        CommunityTarget target = createDefault(ip, community);
        Snmp snmp = null;
        try {
            PDU pdu = new PDU();

            for(String oid:oidList)
            {
                pdu.add(new VariableBinding(new OID(oid)));
            }

            DefaultUdpTransportMapping transport = new DefaultUdpTransportMapping();
            snmp = new Snmp(transport);
            snmp.listen();
            System.out.println("-------> 发送PDU <-------");
            pdu.setType(PDU.GET);
            ResponseEvent respEvent = snmp.send(pdu, target);
            System.out.println("PeerAddress:" + respEvent.getPeerAddress());
            PDU response = respEvent.getResponse();

            if (response == null) {
                System.out.println("response is null, request time out");
            } else {

                System.out.println("response pdu size is " + response.size());
                for (int i = 0; i < response.size(); i++) {
                    VariableBinding vb = response.get(i);
                    System.out.println(vb.getOid() + " = " + vb.getVariable());
                }

            }
            System.out.println("SNMP GET one OID value finished !");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("SNMP Get Exception:" + e);
        } finally {
            if (snmp != null) {
                try {
                    snmp.close();
                } catch (IOException ex1) {
                    snmp = null;
                }
            }

        }
    }


//	public NetEventEntry getNetEventsEntry(Set<String> oidSet) {
//		NetEventEntry neEntry = new NetEventEntry();
//
//		if (null == oidSet) {
//			return null;
//		}
//
//		if (oidSet.isEmpty()) {
//			oidSet.add("1.3.6.1.4.1.9.9.147.1.2.1.1.1.2.5");
//			oidSet.add("1.3.6.1.4.1.9.9.147.1.2.1.1.1.2.6");
//		}
//
//		Map<String, String> vbs = getNext(oidSet);
//
//		if (null == vbs || vbs.isEmpty()) {
//			return null;
//		}
//
//		oidSet.clear();
//		oidSet.addAll(vbs.keySet());
//
//		for (String oid : vbs.keySet()) {
//			logger.info("vbs.keySet()[OID] is: " + oid);
//			if (oid.startsWith("1.3.6.1.4.1.9.9.147.1.2.1.1.1.2.6")) {
//				neEntry.setNeIndex(vbs.get(oid));
//			} else if (oid.startsWith("1.3.6.1.4.1.9.9.147.1.2.1.1.1.2.7")) {
//				neEntry.setNeInterface(vbs.get(oid));
//			} else {
//				return null;
//			}
//		}
//		logger.info("end");
//
//		return neEntry;
//	}

//	public static void main(String[] args) throws Exception {
//		SnmpClient snmp = SnmpClient.create("v2c").setHost("127.0.0.1").setPort("161").setCommunity("public");
//
//		try {
//			snmp.connect();
////			Map<String, IfEntry> s=snmp.walkIfTable();
//			snmp.walkIpAddrTable();
////			System.out.println(s);
//		} finally {
//			snmp.close();
//		}
//	}
}
